// 基于阻塞队列的生产者消费者模型的实现目标：
// 1.使用阻塞队列作为缓冲区存放数据，实现生产者与消费者的解耦
// 2.实现生产者与消费的互斥，既生产者与消费者不能同时访问缓冲区
// 3.实现生产者与消费者的同步，既生产与消费需节奏匹配，缓冲区满了生产者必须等待，缓冲区空了消费者必须等待
#pragma once
#include <queue>
#include <iostream>
#include <pthread.h>
using namespace std;

// 设定阻塞队列的空间大小为5
#define CAP 5

template <typename T>
class blockqueue
{
public:
    blockqueue()
        : _capacity(CAP), _sleep_c(0), _sleep_p(0)
    {
        pthread_cond_init(&_empty_cond, nullptr);
        pthread_cond_init(&_full_cond, nullptr);
        pthread_mutex_init(&mutex, nullptr);
    }
    ~blockqueue()
    {
        pthread_cond_destroy(&_empty_cond);
        pthread_cond_destroy(&_full_cond);
        pthread_mutex_destroy(&mutex);
    }

    queue<T> &bq()
    {
        return _q;
    }

    // 生产
    void Equeuq(T &num)
    {
        pthread_mutex_lock(&mutex);
        while (Full())
        {
            _sleep_p++;
            // 空间放满了，进入等待
            pthread_cond_wait(&_full_cond, &mutex);
            // 问题1: pthread_cond_wait是函数，有可能失败，失败后pthread_cond_wait立即返回

            // 问题2：pthread_cond_wait可能会因为条件不满足，而导致pthread_cond_wait伪唤醒
            // 伪唤醒指：线程从 pthread_cond_wait 返回时，并没有收到其他线程的 pthread_cond_signal 或 pthread_cond_broadcast 通知
            // 且等待的条件可能仍然不满足。这可能由操作系统内核调度、信号中断等底层机制导致
            // 需要使用循环判断是否为伪唤醒：伪唤醒意味着消费者并没有消费，空间仍然为满，循环判断后继续进行队列种等待，若不是伪唤醒则空间不满跳出循环
            _sleep_p--;
        }
        // 没有满，放入数据
        _q.push(num);

        // 已经有数据了，唤醒休眠的消费者
        if (_sleep_c)
        {
            pthread_cond_signal(&_empty_cond);
            cout << "唤醒消费者\n";
        }
        pthread_mutex_unlock(&mutex);
    }

    // 消费
    T Pop()
    {
        pthread_mutex_lock(&mutex);
        while (Empty())
        {
            _sleep_c++;
            // 空了，进入等待，直到生产者生产数据
            pthread_cond_wait(&_empty_cond, &mutex);
            _sleep_c--;
        }
        // 没有空，消费数据
        T num;
        num = _q.front();
        _q.pop();

        // 空间不为满，唤醒休眠的生产者
        if (_sleep_p)
        {
            pthread_cond_signal(&_full_cond);
            cout << "唤醒生产者\n";
        }
        pthread_mutex_unlock(&mutex);
        return num;
    }

private:
    // 判断空间是否满了
    bool Full()
    {
        if (_capacity == _q.size())
            return true;
        return false;
    }

    // 判断空间是否空了
    bool Empty()
    {
        if (_q.size() == 0)
            return true;
        return false;
    }

    queue<T> _q;   // 使用队列作为实现阻塞队列的基础
    int _capacity; // 容量大小

    // 条件变量
    pthread_cond_t _empty_cond;
    pthread_cond_t _full_cond;
    // 锁
    pthread_mutex_t mutex;
    // 休眠数量
    int _sleep_p;
    int _sleep_c;
};